package cn.kinoko.service.manga.impl;

import cn.kinoko.common.base.model.BaseServiceImpl;
import cn.kinoko.common.code.ErrorCodeEnum;
import cn.kinoko.common.utils.ExceptionUtil;
import cn.kinoko.mapper.manga.MangaProcTaskMapper;
import cn.kinoko.model.manga.constant.EpCutoutStatus;
import cn.kinoko.model.manga.constant.EpMarkStatus;
import cn.kinoko.model.manga.constant.ProcTaskStatus;
import cn.kinoko.model.manga.constant.ProcTaskType;
import cn.kinoko.model.manga.entity.MangaEp;
import cn.kinoko.model.manga.entity.MangaEpRaw;
import cn.kinoko.model.manga.entity.MangaProcTask;
import cn.kinoko.service.manga.MangaAIService;
import cn.kinoko.service.manga.MangaEpRawService;
import cn.kinoko.service.manga.MangaEpService;
import cn.kinoko.service.manga.MangaProcTaskService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * @author kinoko
 */
@Slf4j
@Service
public class MangaProcTaskServiceImpl extends BaseServiceImpl<MangaProcTaskMapper, MangaProcTask> implements MangaProcTaskService {

    @Resource
    private MangaEpService mangaEpService;
    @Resource
    private MangaEpRawService mangaEpRawService;
    @Resource
    private MangaAIService mangaAIService;
    @Lazy
    @Resource
    private MangaProcTaskService mangaProcTaskService;

    /**
     * 标记申请
     *
     * @param epId     章节id
     * @param taskType 任务类型
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean submitTask(Long epId, String taskType) {
        MangaEp mangaEp = mangaEpService.getById(epId);
        ExceptionUtil.isNull(mangaEp, ErrorCodeEnum.SYSTEM_ERROR_B0401);
        // 查询是否已存在待执行的任务
        boolean taskExists = lambdaQuery()
                .eq(MangaProcTask::getEpId, epId)
                .eq(MangaProcTask::getTaskType, taskType)
                .in(MangaProcTask::getStatus, ProcTaskStatus.WAITING, ProcTaskStatus.RUNNING)
                .exists();
        ExceptionUtil.assertion(taskExists, ErrorCodeEnum.SYSTEM_ERROR_B0410);
        switch (taskType) {
            case ProcTaskType.MARK -> {
                // 将章节标注状态置为待标记
                mangaEp.setMarkStatus(EpMarkStatus.WAITING.getCode());
                // 更新章节下生肉页码状态
                mangaEpRawService.lambdaUpdate()
                        .eq(MangaEpRaw::getEpId, epId)
                        .eq(MangaEpRaw::getMarkStatus, EpMarkStatus.NOT_APPLY.getCode())
                        .set(MangaEpRaw::getMarkStatus, EpMarkStatus.WAITING.getCode())
                        .update();
            }
            case ProcTaskType.CUTOUT -> {
                // 将章节裁切状态置为待裁切
                mangaEp.setCutoutStatus(EpCutoutStatus.WAITING.getCode());
                // 更新章节下生肉页码状态
                mangaEpRawService.lambdaUpdate()
                        .eq(MangaEpRaw::getEpId, epId)
                        .eq(MangaEpRaw::getCutoutStatus, EpCutoutStatus.NOT_APPLY.getCode())
                        .set(MangaEpRaw::getCutoutStatus, EpCutoutStatus.WAITING.getCode())
                        .update();
            }
        }
        mangaEpService.updateById(mangaEp);
        // 提交标注任务
        MangaProcTask task = new MangaProcTask();
        task.setTaskType(taskType);
        task.setEpId(epId);
        task.setMangaId(mangaEp.getMangaId());
        return save(task);
    }

    /**
     * 标记漫画
     */
    @Override
    public void markManga() {
        if (!mangaAIService.available()) return;
        // 扫描10条待标记任务
        List<MangaProcTask> taskList = findTasksByType(ProcTaskType.MARK);
        for (MangaProcTask task : taskList) {
            if (!mangaAIService.available()) return;
            Long epId = task.getEpId();
            if (handleEpNotExist(task)) continue;
            // 标记中
            mangaProcTaskService.setEpTaskMarkStatus(epId, EpMarkStatus.MARKING, task, ProcTaskStatus.RUNNING);
            // 查询章节待标注的生肉列表
            List<MangaEpRaw> epRaws = mangaEpRawService.lambdaQuery()
                    .eq(MangaEpRaw::getEpId, epId)
                    .eq(MangaEpRaw::getMarkStatus, EpMarkStatus.WAITING.getCode())
                    .list();
            // 标记
            for (MangaEpRaw epRaw : epRaws) {
                // 每次循环都检查一下服务是否可用
                if (!mangaAIService.available()) {
                    // 等待执行
                    mangaProcTaskService.setEpTaskMarkStatus(epId, EpMarkStatus.WAITING, task, ProcTaskStatus.WAITING);
                    return;
                }
                try {
                    mangaAIService.ocrMark(epRaw);
                } catch (Exception e) {
                    log.error("[markManga] ai标记失败，epId：{}", epId, e);
                }
            }
            // 任务完成
            mangaProcTaskService.setEpTaskMarkStatus(epId, EpMarkStatus.DONE, task, ProcTaskStatus.DONE);
        }
    }

    /**
     * 漫画扣字
     */
    @Override
    public void cutoutManga() {
        if (!mangaAIService.available()) return;
        // 扫描10条扣字任务
        List<MangaProcTask> taskList = findTasksByType(ProcTaskType.CUTOUT);
        for (MangaProcTask task : taskList) {
            if (!mangaAIService.available()) return;
            Long epId = task.getEpId();
            // 查询章节信息
            if (handleEpNotExist(task)) continue;
            // 抠图中
            mangaProcTaskService.setEpTaskCutoutStatus(epId, EpCutoutStatus.CUTTING, task, ProcTaskStatus.RUNNING);
            // 查询章节待抠图的生肉列表
            List<MangaEpRaw> epRaws = mangaEpRawService.lambdaQuery()
                    .eq(MangaEpRaw::getEpId, epId)
                    .eq(MangaEpRaw::getCutoutStatus, EpCutoutStatus.WAITING.getCode())
                    .list();
            // 抠图
            for (MangaEpRaw epRaw : epRaws) {
                // 每次循环都检查一下服务是否可用
                if (!mangaAIService.available()) {
                    // 等待执行
                    mangaProcTaskService.setEpTaskCutoutStatus(epId, EpCutoutStatus.WAITING, task, ProcTaskStatus.WAITING);
                    return;
                }
                try {
                    mangaAIService.cutoutText(epRaw);
                } catch (Exception e) {
                    log.error("[markManga] ai抠图失败，epId：{}", epId, e);
                }
            }
            // 任务完成
            mangaProcTaskService.setEpTaskCutoutStatus(epId, EpCutoutStatus.DONE, task, ProcTaskStatus.DONE);
        }
    }

    /**
     * 查询任务
     *
     * @param taskType 任务类型
     * @return 任务列表
     */
    private List<MangaProcTask> findTasksByType(String taskType) {
        return lambdaQuery()
                .eq(MangaProcTask::getTaskType, taskType)
                .in(MangaProcTask::getStatus, ProcTaskStatus.WAITING, ProcTaskStatus.RUNNING)
                .orderByAsc(MangaProcTask::getTaskId)
                .last("limit 10")
                .list();
    }

    /**
     * 处理章节不存在的情况
     *
     * @param task 任务
     * @return 是否处理成功
     */
    private boolean handleEpNotExist(MangaProcTask task) {
        // 查询章节信息
        MangaEp mangaEp = mangaEpService.getById(task.getEpId());
        if (mangaEp == null) {
            log.error("[markManga] 不存在章节，epId：{}", task.getEpId());
            // 完成任务
            task.setStatus(ProcTaskStatus.DONE);
            updateById(task);
            return true;
        }
        return false;
    }

    /**
     * 设置任务和章节的标注状态
     *
     * @param epId    章节id
     * @param status  标注状态
     * @param task    任务
     * @param running 运行状态
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setEpTaskMarkStatus(Long epId, EpMarkStatus status, MangaProcTask task, String running) {
        // 更新章节状态
        mangaEpService.lambdaUpdate().eq(MangaEp::getEpId, epId)
                .set(MangaEp::getMarkStatus, status.getCode())
                .update();
        // 更新任务状态
        task.setStatus(running);
        updateById(task);
    }

    /**
     * 设置任务和章节的标注状态
     *
     * @param epId    章节id
     * @param status  标注状态
     * @param task    任务
     * @param running 运行状态
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setEpTaskCutoutStatus(Long epId, EpCutoutStatus status, MangaProcTask task, String running) {
        // 更新章节状态
        mangaEpService.lambdaUpdate().eq(MangaEp::getEpId, epId)
                .set(MangaEp::getCutoutStatus, status.getCode())
                .update();
        // 更新任务状态
        task.setStatus(running);
        updateById(task);
    }

}